package com.quanyan.orderpay.web;

import com.quanyan.api.APIResponse;
import com.quanyan.api.enums.ApiResponseEnum;
import com.quanyan.common.utils.*;
import com.quanyan.order.exception.OrderExceptionEnum;
import com.quanyan.orderpay.component.OrderPayRedisTemplate;
import com.quanyan.orderpay.constants.OrderMqConstants;
import com.quanyan.orderpay.entity.PaySuccessData;
import com.quanyan.orderpay.service.OrderProcessService;
import com.quanyan.orderpay.vo.request.DecodeUserInfoParamsVo;
import com.quanyan.orderpay.vo.request.OpenIdAndSessionParamsVo;
import com.quanyan.orderpay.vo.response.OpenIdAndSessionRespVo;
import com.quanyan.pay.entity.TSNRecord;
import com.quanyan.pay.entity.WeichatAccount;
import com.quanyan.pay.enums.TradeTypeEnum;
import com.quanyan.pay.service.PayService;
import com.quanyan.pay.service.TsnService;
import com.quanyan.pay.service.WeichatAccountService;
import com.quanyan.weichat.api.WeichatServiceApi;
import com.quanyan.weichat.common.Configure;
import com.quanyan.weichat.common.RandomStringGenerator;
import com.quanyan.weichat.common.SHA1;
import com.quanyan.weichat.common.Signature;
import com.quanyan.weichat.protocol.common_protocal.WeichatJSConfig;
import com.quanyan.weichat.util.AESUtils;
import com.quanyan.weichat.vo.OpenIdAndSessionVo;
import com.quanyan.weichat.vo.WxUserInfo;
import net.sf.json.JSONObject;
import org.hibernate.validator.constraints.NotBlank;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * 接收微信服务器回调
 *
 * @author songzj
 * @date 16/5/26-12:00
 */
@Controller(value = "weichatController")
public class WeichatController {
    private Logger logger = LoggerFactory.getLogger(WeichatController.class);

    @Autowired
    private OrderPayRedisTemplate orderPayRedisTemplate;
    @Autowired
    private OrderProcessService OrderProcessService;
    @Autowired
    private TsnService tsnService;
    @Autowired
    private PayService payService;
    @Autowired
    private WeichatAccountService weichatAccountService;

    private static String[] jsApiList;

    /**
     * 微信支付网关回调
     * <xml><appid><![CDATA[wxf45cb3432988261f]]></appid><bank_type><![CDATA[GDB_CREDIT]]></bank_type><cash_fee><![CDATA[88]]></cash_fee><device_info><![CDATA[WEB]]></device_info><fee_type><![CDATA[CNY]]></fee_type><is_subscribe><![CDATA[Y]]></is_subscribe><mch_id><![CDATA[1357152002]]></mch_id><nonce_str><![CDATA[aote1fkilr0q6vn7k0ha8toa3zrn159b]]></nonce_str><openid><![CDATA[o5p0hwDiTg8Q8TTkW6GYtlWZVcPA]]></openid><out_trade_no><![CDATA[21747982850900000142]]></out_trade_no><result_code><![CDATA[SUCCESS]]></result_code><return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[A4DD71D7E4B1F9FFF2867D901681585F]]></sign><time_end><![CDATA[20160622221049]]></time_end><total_fee>88</total_fee><trade_type><![CDATA[JSAPI]]></trade_type><transaction_id><![CDATA[4000162001201606227723069757]]></transaction_id></xml>
     *
     * @return
     */
    @RequestMapping("/wechat/paySuccess.api")
    public void paySuccess(HttpServletRequest request, HttpServletResponse response) {
        String requestXmlString = ServletUtils.getRequestInputStream2String(request);
        try {
            Map<String, String> map = ObjectUtil.convertXmlToMap(requestXmlString);
            if (map == null) {
                logger.warn("invalid pay call!!!!!，requestXmlString=" + requestXmlString);
                return;
            }
            logger.warn("订单号：{}。微信支付成功回调： {}", map.get("out_trade_no"), requestXmlString);

            String appId = map.get("appid");
            String mchId = map.get("mch_id");
            APIResponse<Configure> accountAPIResponse = this.payService.getWeichatAccount(appId, mchId);
            if (!accountAPIResponse.isRet()) {
                logger.error("appid:" + appId + ",mch_id: " + mchId + " 在微信账号表中不存在!");
                return;
            }

            if (!Signature.isValidSign(map, accountAPIResponse.getData().getKey())) {
                logger.warn("recieve tencent pay invalid sign !" + JSONObject.fromObject(map).toString());
                return;
            }

            String result_code = map.get("result_code"); //业务结果
            String return_code = map.get("return_code"); //通信状态码
            PaySuccessData paySuccessData = new PaySuccessData();
            paySuccessData.setSellerId(map.get("mch_id"));
            paySuccessData.setSellerAccount(map.get("appid"));
            paySuccessData.setBuyerAccount(map.get("openid"));
            paySuccessData.setBuyerBank(map.get("bank_type"));
            paySuccessData.setBuyerId(null);
            paySuccessData.setOrderNo(map.get("out_trade_no"));
            paySuccessData.setTsn(map.get("transaction_id"));
            paySuccessData.setPayAmount(new BigDecimal(map.get("total_fee")).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_DOWN));
            paySuccessData.setPayTime(DateTimeUtils.convertString2Date("yyyyMMddHHmmss", map.get("time_end")));
            paySuccessData.setTradeType(TradeTypeEnum.valueOf("TRADE_TYPE_WEICHAT_" + map.get("trade_type")));
            paySuccessData.setSourceType(OrderMqConstants.PAY_RESEND);
            TSNRecord tsnRecord = this.tsnService.insertTsnRecord(paySuccessData.getTradeType().getPayId(),
                    TsnService.DATA_TYPE_XML, requestXmlString, paySuccessData.getTsn(),
                    paySuccessData.getOrderNo(), TsnService.TRASACTION_PAY);
            if ("SUCCESS".equals(result_code)) { //支付成功
                boolean flag = this.OrderProcessService.paySuccess(paySuccessData);
                if (flag) {
                    logger.info("订单号：{}。微信支付成功，回调成功。", map.get("out_trade_no"));
                    this.tsnService.updateTsnRecordStatus(tsnRecord.getId(), TsnService.STATUS_PROCESS_SUCCESS);
                    Map<String, Object> resultMap = new HashMap<>();
                    resultMap.put("return_code", "SUCCESS");
                    resultMap.put("return_msg", "OK");
                    ServletUtils.response(response, ObjectUtil.mapToXMLString(resultMap));
                }
            }
        } catch (Exception e) {
            logger.error("处理微信支付成功回调异常。requestXmlString:" + requestXmlString, e);
        }
    }


    /**
     * 获取微信jsApi配置属性.
     * url 当前网页的URL，不包含#及其后面部分
     * 微信文档地址   https://mp.weixin.qq.com/wiki/11/74ad127cc054f6b80759c40f77ec03db.html#.E9.99.84.E5.BD.951-JS-SDK.E4.BD.BF.E7.94.A8.E6.9D.83.E9.99.90.E7.AD.BE.E5.90.8D.E7.AE.97.E6.B3.95
     * @return
     */
    @RequestMapping("/api/pay/h5/wxConfig")
    @ResponseBody
    public APIResponse<WeichatJSConfig> wxConfig(String mchId, Integer payId, String url) {
        if (StringUtil.isBlankOrNull(mchId)) {
            return APIResponse.returnFail("mchId不能为空");
        }
        if (payId == null) {
            return APIResponse.returnFail("payId不能为空");
        }

        logger.warn("method-wxConfig:" + "mchid-" + mchId + "  payId-" + payId);
        //url 当前网页的URL，不包含#及其后面部分
        WeichatJSConfig jsConfig = new WeichatJSConfig(true, jsApiList, url);

        WeichatAccount weichatAccount = weichatAccountService.getWeichatConfigure(mchId, payId);

        String appId = weichatAccount.getAppId();
        String securityKey = weichatAccount.getSecurityKey();
        logger.warn("method-wxConfig:" + "appId-" + appId + "  securityKey-" + securityKey);

        String access_token = orderPayRedisTemplate.hget("token_" + appId, "access_token");

        String ticket = orderPayRedisTemplate.hget("ticket_" + appId, "ticket");

        logger.warn("access_token>>>>>>>>>>>>>:" + access_token);
        logger.warn("ticket>>>>>>>>>>>>>:" + ticket);

        String nonceStr = RandomStringGenerator.getRandomStringByLength(32);
        String timestamp = "" + (System.currentTimeMillis() / 1000);
        jsConfig.setTimestamp(timestamp);
        jsConfig.setNonceStr(nonceStr);
        jsConfig.setAppId(appId);
        String string = "jsapi_ticket=" + ticket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url;
        jsConfig.setSignature(new SHA1().getDigestOfString(string.getBytes()).toLowerCase());
        return APIResponse.returnSuccess(jsConfig);
    }

    /**
     * 获取微信AppId.
     * @return
     */
    @RequestMapping("/api/pay/h5/getAppId")
    @ResponseBody
    public APIResponse<String> getAppId(String mchId, Integer payId) {
        if (StringUtil.isBlankOrNull(mchId)) {
            return APIResponse.returnFail("mchId不能为空");
        }
        if (payId == null) {
            return APIResponse.returnFail("payId不能为空");
        }
        logger.info("method-getAppId:" + "mchid-" + mchId + "  payId-" + payId);
        WeichatAccount weichatAccount = weichatAccountService.getWeichatConfigure(mchId, payId);
        return APIResponse.returnSuccess(weichatAccount.getAppId());

    }

    /**
     * H5支付 获取openId
     *
     * @return
     */
    @RequestMapping(value = "/api/pay/h5/getOpenId")
    @ResponseBody
    public APIResponse<String> getOpenId(String mchId, Integer payId, String code) {
        if (StringUtil.isBlankOrNull(mchId)) {
            return APIResponse.returnFail("mchId不能为空");
        }
        if (payId == null) {
            return APIResponse.returnFail("payId不能为空");
        }
        if (StringUtil.isBlankOrNull(code)) {
            return APIResponse.returnFail("code不能为空");
        }

        String openid = "";

        WeichatAccount weichatAccount = weichatAccountService.getWeichatConfigure(mchId, payId);
        if (weichatAccount == null) {
            return APIResponse.returnFail("payId或mchId不正确");
        }

        WeichatServiceApi api = new WeichatServiceApi();
        Map<String, String> resp = api.getUserOpenId(weichatAccount.getAppId(), weichatAccount.getSecurityKey(), code);
        if (resp != null && resp.size() != 0) {
            openid = resp.get(WeichatServiceApi.OPEN_ID);
        }
        logger.info("获取openid。请求：mchId={}，payId={}，code={}。晌应：{}。", mchId, payId, code, resp);
        if (StringUtil.isBlankOrNull(openid)) {
            return APIResponse.returnFail("可能code没传对或code已过期或微信服务器异常。请求参数：code=" + code);
        }
        if (StringUtil.isNotBlank(openid)) {
            orderPayRedisTemplate.hmset(weichatAccount.getAppId() + "_" + openid, resp);//key: appId_openId
        }

        return APIResponse.returnSuccess(openid);
    }

    /**
     * 获取用户网页授权下的用户信息(scope为snsapi_base)
     * PS:若用户未关注该公众号，则只能返回openid和unionid, 若用户已关注，则能返回更详细的用户信息，如openid、unionid、头图、昵称等
     * @return
     */
    @RequestMapping(value = "/api/pay/h5/getUserInfoByCode")
    @ResponseBody
    public APIResponse getUserInfoByCode(String mchId, Integer payId, String code) {
        if (StringUtil.isBlankOrNull(code)) {
            return APIResponse.returnFail("code不能为空");
        }
        if (payId == null) {
            return APIResponse.returnFail("payId不能为空");
        }
        if (StringUtil.isBlankOrNull(mchId)) {
            return APIResponse.returnFail("mchId不能为空");
        }

        String openid = "";
        String unionid = "";

        WeichatAccount weichatAccount = weichatAccountService.getWeichatConfigure(mchId, payId);
        if (weichatAccount == null) {
            return APIResponse.returnFail("payId或mchId不正确");
        }

        WeichatServiceApi api = new WeichatServiceApi();
        Map<String, String> resp = api.getUserOpenId(weichatAccount.getAppId(), weichatAccount.getSecurityKey(), code);
        if (resp != null && resp.size() != 0) {
            openid = resp.get(WeichatServiceApi.OPEN_ID);
            unionid = resp.get(WeichatServiceApi.UNION_ID);
        }
        logger.info("获取openid。请求：mchId={}，payId={}，code={}。晌应：{}。", mchId, payId, code, resp);
        if (StringUtil.isBlankOrNull(openid)) {
            return APIResponse.returnFail("可能code没传对或code已过期或微信服务器异常。请求参数：code=" + code);
        }
        if (StringUtil.isNotBlank(openid)) {
            orderPayRedisTemplate.hmset(weichatAccount.getAppId() + "_" + openid, resp);//key: appId_openId
        }

        String globalAccessToken = orderPayRedisTemplate.hget("token_" + weichatAccount.getAppId(), "access_token");
        WxUserInfo wxUserInfo = new WxUserInfo();
        if (StringUtil.isNotBlank(globalAccessToken)) {
            wxUserInfo = api.getUserInfos(openid, globalAccessToken);
        }
        wxUserInfo.setOpenid(openid);
        wxUserInfo.setUnionid(unionid);

        return APIResponse.returnSuccess(wxUserInfo);
    }


    /**
     * 获取用户网页授权下的用户信息(scope为snsapi_userinfo)
     * @return
     */
    @RequestMapping(value = "/api/pay/h5/getAccreditUserInfoByCode")
    @ResponseBody
    public APIResponse getAccreditUserInfoByCode(String mchId, Integer payId, String code) {
        if (StringUtil.isBlankOrNull(mchId)) {
            return APIResponse.returnFail("mchId不能为空");
        }
        if (payId == null) {
            return APIResponse.returnFail("payId不能为空");
        }
        if (StringUtil.isBlankOrNull(code)) {
            return APIResponse.returnFail("code不能为空");
        }

        String openid = "";
        String accessToken = "";

        WeichatAccount weichatAccount = weichatAccountService.getWeichatConfigure(mchId, payId);
        if (weichatAccount == null) {
            return APIResponse.returnFail("payId或mchId不正确");
        }

        WeichatServiceApi api = new WeichatServiceApi();
        Map<String, String> resp = api.getUserOpenId(weichatAccount.getAppId(), weichatAccount.getSecurityKey(), code);
        if (resp != null && resp.size() != 0) {
            openid = resp.get(WeichatServiceApi.OPEN_ID);
            accessToken = resp.get(WeichatServiceApi.ACCESS_TOKEN);
        }
        logger.info("获取openid。请求：mchId={}，payId={}，code={}。晌应：{}。", mchId, payId, code, resp);
        if (StringUtil.isBlankOrNull(openid)) {
            return APIResponse.returnFail("可能code没传对或code已过期或微信服务器异常。请求参数：code=" + code);
        }
        if (StringUtil.isNotBlank(openid)) {
            orderPayRedisTemplate.hmset(weichatAccount.getAppId() + "_" + openid, resp);//key: appId_openId
        }
        WxUserInfo wxUserInfo = api.getAccreditUserInfo(accessToken, openid);
        if (StringUtil.isBlankOrNull(wxUserInfo.getOpenid())) {
            logger.info("获取微信用户信息openId为null");
            return APIResponse.returnFail(OrderExceptionEnum.NEED_AFRESH_ACCREDIT);
        }
        return APIResponse.returnSuccess(wxUserInfo);
    }


    /**
     * 获取用户网页授权下的用户信息
     * @return
     */
    @RequestMapping(value = "/api/pay/h5/getAccreditUserInfo")
    @ResponseBody
    public APIResponse getAccreditUserInfo(String appId, String openId) {
        if (StringUtil.isBlankOrNull(appId)) {
            return APIResponse.returnFail("appId不能为空");
        }
        if (StringUtil.isBlankOrNull(openId)) {
            return APIResponse.returnFail("openId不能为空");
        }
        WeichatServiceApi api = new WeichatServiceApi();
        String accessToken = orderPayRedisTemplate.hget(appId + "_" + openId, "access_token");
        logger.info("appId={},openId={},accessToken={}", appId, openId, accessToken);
        if (StringUtil.isBlankOrNull(accessToken)) {
            logger.info("获取accessToken为null");
            return APIResponse.returnFail(OrderExceptionEnum.NEED_AFRESH_ACCREDIT);
        }
        WxUserInfo wxUserInfo = api.getAccreditUserInfo(accessToken, openId);
        if (StringUtil.isBlankOrNull(wxUserInfo.getOpenid())) {
            logger.info("获取微信用户信息openId为null");
            return APIResponse.returnFail(OrderExceptionEnum.NEED_AFRESH_ACCREDIT);
        }
        return APIResponse.returnSuccess(wxUserInfo);
    }

    /**
     * 微信小程序：根据微信授权code获取openid和百灵鸟session
     * 可参考文档  http://www.cnblogs.com/nosqlcoco/p/6105749.html
     * @return
     */
    @RequestMapping(value = "/api/pay/applet/getOpenIdAndSession")
    @ResponseBody
    public APIResponse<OpenIdAndSessionRespVo> getOpenIdAndSession(@RequestBody OpenIdAndSessionParamsVo paramsVo) {
        ValidationResult result = ValidationUtils.validateEntity(paramsVo);
        if (result.hasErrors()) {
            return APIResponse.returnFail(ApiResponseEnum.PARAMETER_INVALID, result.getErrorDesc());
        }
        String mchId = paramsVo.getMchId();
        Integer payId = paramsVo.getPayId();
        String code = paramsVo.getCode();
        WeichatAccount weichatAccount = weichatAccountService.getWeichatConfigure(mchId, payId);
        if (weichatAccount == null) {
            return APIResponse.returnFail("payId或mchId不正确");
        }
        WeichatServiceApi api = new WeichatServiceApi();
        OpenIdAndSessionVo vo = api.getOpenIdAndSession(weichatAccount.getAppId(), weichatAccount.getSecurityKey(), code);
        if (vo == null) {
            return APIResponse.returnFail("访问微信服务器异常。");
        }
        if (vo.getErrcode() != null) {
            return APIResponse.returnFail("访问微信服务器接口返回信息如下：" + GsonUtils.toJson(vo));
        }

        String blnWxAppletSessionKey = StringUtil.genRandomCode(128, false).toUpperCase();
        orderPayRedisTemplate.hmset("BLN_WX_APPLET_SESSION_KEY" + "_" + blnWxAppletSessionKey, ObjectUtil.objectToMapString(vo), 20*24*3600); // 20天

        OpenIdAndSessionRespVo respVo = new OpenIdAndSessionRespVo();
        respVo.setOpenid(vo.getOpenid());
        respVo.setBlnWxAppletSessionKey(blnWxAppletSessionKey);
        return APIResponse.returnSuccess(respVo);
    }

    /**
     * 微信小程序：解密用户信息
     * 微信解密算法文档地址  https://mp.weixin.qq.com/debug/wxadoc/dev/api/signature.html
     * @return
     */
    @RequestMapping(value = "/api/pay/applet/decodeUserInfo")
    @ResponseBody
    public APIResponse<WxUserInfo> decodeUserInfo(@RequestBody DecodeUserInfoParamsVo paramsVo) {
        ValidationResult result = ValidationUtils.validateEntity(paramsVo);
        if (result.hasErrors()) {
            return APIResponse.returnFail(ApiResponseEnum.PARAMETER_INVALID, result.getErrorDesc());
        }
        String blnWxAppletSessionKey = paramsVo.getBlnWxAppletSessionKey();
        String encryptedData = paramsVo.getEncryptedData();
        String iv = paramsVo.getIv();
        String rawData = paramsVo.getRawData();
        String signature = paramsVo.getSignature();

        String sessionKey = orderPayRedisTemplate.hget("BLN_WX_APPLET_SESSION_KEY" + "_" + blnWxAppletSessionKey, "session_key");
        if (StringUtil.isBlankOrNull(sessionKey)) {
            return APIResponse.returnFail(7001, "blnWxAppletSessionKey不存在或已过期，请重新授权。");
        }
        String signatureCheck = EncryptUtils.hashBySHA1(rawData + sessionKey);
        if (!signature.equals(signatureCheck)) {
            return APIResponse.returnFail(7002, "验签失败。可能原因：1、非法请求；2、session已过期，请重新授权。");
        }
        byte[] decodeDataBytes = AESUtils.decrypt(Base64Utils.decode(encryptedData.getBytes()), Base64Utils.decode(sessionKey.getBytes()), Base64Utils.decode(iv.getBytes()));
        String decodeData = "";
        try {
            decodeData = new String(decodeDataBytes, "UTF-8");
        } catch (UnsupportedEncodingException e) {
        }
        com.alibaba.fastjson.JSONObject jo = com.alibaba.fastjson.JSONObject.parseObject(decodeData);
        WxUserInfo userInfo = new WxUserInfo();
        userInfo.setUnionid(jo.getString("unionId"));
        userInfo.setOpenid(jo.getString("openId"));
        userInfo.setCity(jo.getString("city"));
        userInfo.setCountry(jo.getString("country"));
        userInfo.setHeadimgurl(jo.getString("avatarUrl"));
        userInfo.setNickname(jo.getString("nickName"));
        userInfo.setSex(jo.getInteger("gender"));
        userInfo.setProvince(jo.getString("province"));

        return APIResponse.returnSuccess(userInfo);
    }



    static {
        jsApiList = new String[]{"onMenuShareTimeline",
                "onMenuShareAppMessage",
                "onMenuShareQQ",
                "onMenuShareWeibo",
                "startRecord",
                "stopRecord",
                "onVoiceRecordEnd",
                "playVoice",
                "pauseVoice",
                "stopVoice",
                "onVoicePlayEnd",
                "uploadVoice",
                "downloadVoice",
                "chooseImage",
                "previewImage",
                "uploadImage",
                "downloadImage",
                "translateVoice",
                "getNetworkType",
                "openLocation",
                "getLocation",
                "hideOptionMenu",
                "showOptionMenu",
                "hideMenuItems",
                "showMenuItems",
                "hideAllNonBaseMenuItem",
                "showAllNonBaseMenuItem",
                "closeWindow",
                "scanQRCode",
                "chooseWXPay",
                "openProductSpecificView",
                "addCard",
                "chooseCard",
                "openCard"};
    }

}
